/*
 *  ssdom.cpp
 *
 *  Created by Garrett williams on 5/19/05.
 *  Copyright 2005 Drag'N Flies. All rights reserved.
 *
 */

#include "ssdom.h"
#include <iostream>

SSLeaf::SSLeaf():
    tag(0),
    value(0),
    attributes(0),
    current_attribute(0),
    child(0),
    next(0)
    
{
}

SSLeaf::~SSLeaf()
{
    if(tag)        delete[] tag;
    if(value)      delete[] value;
    if(attributes) delete attributes;
    if(child)      delete child;
    if(next)       delete next;
}

const char* SSLeaf::getTag()
{
    return tag;
}

void SSLeaf::setTag(const char* t)
{
    tag = new char[strlen(t)+1];
    strcpy(tag,t);
}

const char* SSLeaf::getValue()
{
    return value;
}

void SSLeaf::setValue(const char* v)
{
    value = new char[strlen(v)+1];
    strcpy(value,v);
}

void SSLeaf::resetAttribute()
{
    current_attribute = attributes;
}

void SSLeaf::addAttribute(const char* a,const char* v)
{
    SSAttribute *added = new SSAttribute(a,v);
    if(attributes)
        attributes->setNext(added);
    else
        attributes = added;
}

const char* SSLeaf::getAttributeName()
{
    return tag;
}

const char* SSLeaf::getAttributeValue()
{
    return value;
}

SSAttribute* SSLeaf::findAttribute(const char* a)
{
    return attributes->find(a);
}

const char* SSLeaf::getAttributeValue(const char* a)
{
    SSAttribute *f_att;
    f_att = findAttribute(a);
    if(f_att)
        return f_att->getValue();
    return 0;
}

SSLeaf* SSLeaf::getNext()
{
    return next;
}

SSLeaf* SSLeaf::getChild()
{
    return child;
}

void 
SSLeaf::addNext(SSLeaf* leaf)
{
    SSLeaf *n = next;
    next = leaf;
    next->next = n;
}

void 
SSLeaf::addChild(SSLeaf* leaf)
{
    SSLeaf *c = child;
    child = leaf;
    child->next = c;
}

// remove child from this leaf
void
SSLeaf::removeChild()
{
    delete child;
    child = NULL;
}

// remove next leaf from this leaf
void
SSLeaf::removeNext()
{
    delete next;
    next = NULL;
}

SSDOM::SSDOM():
    head(0),
    current(0)
{
}

SSDOM::SSDOM(const char* name):
    head(0),
    current(0)
{
    //try {
        setFile(name,true);
        setUserData(0);
        doParse();
   // } catch(const char* err) {
   //     throw err;
   // }

}

SSDOM::~SSDOM()
{
    // leaves delet their children and siblings.
    if(head) delete head;
}

/*
    clear the last list of leaves
*/
void 
SSDOM::reset()
{
    SSLeaf *tmp;
    while(leaf_stack.pop(tmp));// reset stack watch.
}

// create and add leaf to parent or current or top level.
SSLeaf* 
SSDOM::addNext(SSLeaf *parent)
{
    SSLeaf *leaf = new SSLeaf;
    if(parent)
    {
        leaf->next = parent->next; // keep next list
        parent->next = leaf;
    }
    else if(current)
    {
        leaf->next = current->next; // keep next list
        current->next = leaf;
    }
    else
    {
         head = current = leaf;
    }
    return leaf;
}

SSLeaf* 
SSDOM::addChild(SSLeaf *parent)
{
    SSLeaf *leaf = new SSLeaf;
    if(parent)
    {
        leaf->next = parent->child; // keep child list
        parent->child = leaf;
    }
    else if(current)
    {
        leaf->next = current->child; // keep child list
        parent->child = leaf;
    }
    else
    {
         head = current = leaf;
    }
    return leaf;
}

// delete the next leaf node of the parent
// the child branch of the next node will be deleted also
void
SSDOM::removeNextLeaf(SSLeaf *parent, bool branch)
{
    if(parent && parent->next)
        if(branch)
        {
            delete parent->next; // delete entire branch.
            parent->next = NULL; // terminate branch.
        }
        else
        {
            SSLeaf *hand = parent->next;
            parent->next = hand->next; // shift the branch to the parent
            hand->next = NULL;  // presurve the branch below
            delete hand; // delete the node the child branche will be deleted
        }
}

void
SSDOM::removeChildLeaf(SSLeaf *parent, bool branch)
{
    if(parent && parent->child)
        if(branch)
        {
            delete parent->child; // remove entire branch destructor will handle it.
            parent->child = NULL; // terminate branch.
        }
        else
        {
            SSLeaf *hand = parent->child;
            parent->child = hand->next; // shift the next child branch to the parent
            hand->next = NULL;  // presurve the branch bellow
            delete hand; // delete the node the child branche will be deleted
        }
}

/*
    follow tree down the children and then the siblings.
*/
void 
SSDOM::writeNext(SSLeaf *leaf, short count)
{
    if(leaf)
    {
        writeNext(leaf->next,count);
        writeStartTag(count,leaf->tag);
        if(leaf->attributes)
        {
            SSAttribute *att = leaf->attributes;
            while(att)
            {
                // write attributes.
                writeAttTag(att->getTag());
                writeAttValue(att->getValue());
                att = att->getNext();
            }
        }
        writeStartTagClose();
        if(leaf->value)
        {
            writeData(leaf->value);
        }
        writeNext(leaf->child,count+1);
        writeEndTag(count,leaf->tag);
    }
}

/*
    follow the tree down and return back to the orginal caller when 
    the tag is found.
*/
SSLeaf* SSDOM::searchNext(SSLeaf *leaf, const char* tag, bool start)
{
    if(leaf) // dead end quit the trail.
    {
        SSLeaf *rt;
        if(0 == strcmp(leaf->tag,tag))         // check the current leaf for the tag.
            return leaf;                       // this is the leaf with the tag return it.
        rt = searchNext(leaf->child,tag);      // check the children for the tag.
        if(rt) return rt;                      // did a child contain the tag?
        if(!start)                             // only search below starting leaf.
            return searchNext(leaf->next,tag); // following siblings
    }
    return 0;
}

// stack to put leaves in, leaf to start with, tag to match to, search only children of this leaf only.
void SSDOM::listNext(SSLeafStack& stack,SSLeaf *leaf, const char* tag, bool start)
{
    if(leaf) // dead end quit the trail.
    {
        if(0 == strcmp(leaf->tag,tag)) // check if this leaf is the tag wanted.
            stack.push(leaf);          // found one put it in the list.
        listNext(stack,leaf->child,tag);     // follow children.
        if(!start) // the first time in this function do not follow siblings.
            listNext(stack,leaf->next,tag);      // follow siblings.
    }
}

void 
SSDOM::write(const char* name)
{ 
    try{
        if(!fileOutOpen())
        {
            if(name)
                setFile(name,true);
            //else
                // throw err;
        }
        readyWrite();
        writeNext(head,0);
    }catch(const char* err){
       // throw err;
    }
    closeOutFile();
}

void SSDOM::startTagCB(void *user_data, const char *tag, SSAttribute *attributes)
{
    if(head)
    {
        leaf_stack.push(current);
        SSLeaf *leaf = new SSLeaf();
        current->addChild(leaf);
        current = current->child;
    }
    else
    {
        current = head = new SSLeaf();
    }
    current->setTag(tag);
    SSAttribute *att = attributes;
    while(att)
    {
        current->addAttribute(att->getTag(),att->getValue());
        att = att->getNext();
    }
}

void SSDOM::charDataCB(void *user_data, const char *data)
{
    current->setValue(data);
}

void SSDOM::endTagCB(void *user_data, const char *tag)
{
    leaf_stack.pop(current);
}

